package emqx

import (
	"bytes"
	"encoding/json"
	"fmt"
	"gitee.com/captials-team/ubdframe/src/common"
	"gitee.com/captials-team/ubdframe/src/common/utils"
	"io"
	"net/http"
)

// EMQXOpenApi 请求api
// 目前适用版本： https://docs.emqx.com/zh/emqx/v5.5/admin/api-docs.html
type EMQXOpenApi struct {
	Uri       string //api请求地址,如： http://192.168.149.128:18083
	ApiKey    string //api请求key
	ApiSecret string //api请求secret
}

func (api *EMQXOpenApi) Version() string {
	return "5.5.0"
}

// PostRule 添加规则
func (api *EMQXOpenApi) PostRule(data interface{}) error {
	client := &http.Client{}

	s, _ := json.Marshal(data)
	req, err := http.NewRequest("POST", api.ApiUrlForRule(), bytes.NewBuffer(s))
	common.ErrPanic(err)
	req.SetBasicAuth(api.ApiKey, api.ApiSecret)
	req.Header.Set("Content-Type", "application/json")
	resp, err := client.Do(req)
	if err != nil {
		return err
	}

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return err
	}

	var res OpenApiResp
	err = json.Unmarshal(body, &res)
	if err != nil {
		return err
	}
	if res.Code != "" {
		return fmt.Errorf("err: %s,%s", res.Code, res.Message)
	}
	return nil
}

// PostAuthentication 添加认证器,id=认证器id
func (api *EMQXOpenApi) PostAuthentication(data interface{}) (int, error) {
	resp, err := api.doRequest(http.MethodPost, api.ApiUrlForAuthentication(""), data)
	if err != nil {
		return http.StatusOK, err
	}

	return api.returnByHttpCode(resp)
}

// DeleteAuthentication 删除认证器,id=认证器id
func (api *EMQXOpenApi) DeleteAuthentication(id string) (int, error) {
	resp, err := api.doRequest(http.MethodDelete, api.ApiUrlForAuthentication(id), map[string]string{})
	if err != nil {
		return http.StatusOK, err
	}

	return api.returnByHttpCode(resp)
}

// PutAuthentication 添加认证器,id=认证器id
func (api *EMQXOpenApi) PutAuthentication(id string, data interface{}) (int, error) {
	resp, err := api.doRequest(http.MethodPut, api.ApiUrlForAuthentication(id), data)
	if err != nil {
		return http.StatusOK, err
	}

	return api.returnByHttpCode(resp)
}

// PostAuthorization 添加授权器
func (api *EMQXOpenApi) PostAuthorization(data interface{}) (int, error) {
	resp, err := api.doRequest(http.MethodPost, api.ApiUrlForAuthorization(""), data)
	if err != nil {
		return http.StatusOK, err
	}

	return api.returnByHttpCode(resp)
}

// PutAuthorization 更新授权器,ty=授权器类型
func (api *EMQXOpenApi) PutAuthorization(ty string, data interface{}) (int, error) {
	resp, err := api.doRequest(http.MethodPut, api.ApiUrlForAuthorization(ty), data)
	if err != nil {
		return http.StatusOK, err
	}

	return api.returnByHttpCode(resp)
}

// DeleteAuthorization 删除授权器
func (api *EMQXOpenApi) DeleteAuthorization(ty string) (int, error) {
	resp, err := api.doRequest(http.MethodDelete, api.ApiUrlForAuthorization(ty), map[string]string{})
	if err != nil {
		return http.StatusOK, err
	}

	return api.returnByHttpCode(resp)
}

// ApiUrlForRule 规则rule操作url
func (api *EMQXOpenApi) ApiUrlForRule() string {
	return api.ApiFullUrl("/api/v5/rules")
}

// ApiUrlForAuthentication 认证器操作url
func (api *EMQXOpenApi) ApiUrlForAuthentication(authId string) string {
	if authId == "" {
		return api.ApiFullUrl(fmt.Sprintf("/api/v5/authentication"))
	}
	return api.ApiFullUrl(fmt.Sprintf("/api/v5/authentication/%s", authId))
}

func (api *EMQXOpenApi) ApiUrlForAuthorization(ty string) string {
	if ty == "" {
		return api.ApiFullUrl(fmt.Sprintf("/api/v5/authorization/sources"))
	}
	return api.ApiFullUrl(fmt.Sprintf("/api/v5/authorization/sources/%s", ty))
}

func (api *EMQXOpenApi) ApiFullUrl(path string) string {
	return fmt.Sprintf("%s%s", api.Uri, utils.KeepHasPrefix(path, "/"))
}

func (api *EMQXOpenApi) doRequest(method, url string, bodyData interface{}) (*http.Response, error) {
	client := &http.Client{}
	var body []byte
	if bodyData != nil {
		s, err := json.Marshal(bodyData)
		if err != nil {
			return nil, err
		}
		body = s
	}

	req, err := http.NewRequest(method, url, bytes.NewBuffer(body))
	common.ErrPanic(err)
	req.SetBasicAuth(api.ApiKey, api.ApiSecret)
	req.Header.Set("Content-Type", "application/json")
	return client.Do(req)
}

func (api *EMQXOpenApi) returnByHttpCode(resp *http.Response) (int, error) {
	if resp == nil {
		return http.StatusOK, fmt.Errorf("response nil")
	}

	if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusIMUsed {
		return resp.StatusCode, fmt.Errorf("code err %d,%s", resp.StatusCode, resp.Status)
	}

	//解析body
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return resp.StatusCode, err
	}
	if len(body) == 0 {
		return resp.StatusCode, nil
	}

	var res OpenApiResp
	err = json.Unmarshal(body, &res)
	if err != nil {
		return resp.StatusCode, fmt.Errorf("err %s,%s", err, string(body))
	}
	return resp.StatusCode, nil
}
